package zdream.control.world;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import zdream.control.region.Room;

import static zdream.control.region.TerrainConstant.*;
import static zdream.control.world.Box.*;

/**
 * 为每个 box 检查地形
 * 
 * @author Zdream
 * @since 0.0.1
 * @date 2021-08-21 (created)
 * @date 2022-05-29 (last modified)
 */
public class TerrainHandler {
	final LevelWorld world;
	public static final List<MFoe> tmp = new ArrayList<>();
	public static final List<MFoe> tmp2 = new ArrayList<>();
	public static final List<MFoe> tmp3 = new ArrayList<>();

	public TerrainHandler(LevelWorld world) {
		this.world = Objects.requireNonNull(world);
	}

	/**
	 * 对应的 Foe 能否攀爬
	 */
	public boolean climbable;
	/**
	 * 将左右两侧房间外的地形视为什么
	 */
	public byte xOutsideTerrain = TERRAIN_EMPTY;
	/**
	 * Foe 是否是漂浮在地形之外, 不受地形束缚的 (可穿墙)
	 */
	public boolean floating;

	/**
	 * <p>获得地形数据.
	 * <li>当 x 出界时, 返回 TERRAIN_SOLID
	 * <li>当 y 向上出界时, 返回 room.terrains[room.height - 1]
	 * <li>当 y 向下出界时, 返回 TERRAIN_EMPTY
	 * </li></p>
	 * @param x
	 *   相对于房间的横坐标, 单位: 块
	 * @param y
	 *   相对于房间的纵坐标, 单位: 块
	 * @return
	 *   地形码
	 */
	public byte getTerrain(int x, int y) {
		Room currentRoom = world.currentRoom;
		if (x < 0 || x >= currentRoom.width) {
			return xOutsideTerrain;
		}
		if (y >= currentRoom.height) {
			return currentRoom.terrains[x][currentRoom.height - 1];
		}
		if (y < 0) {
			return TERRAIN_EMPTY;
		}
		return currentRoom.terrains[x][y];
	}

	/**
	 * <p>获得填充物数据, 比如水、沼泽 / 沙地就是填充物
	 * </li></p>
	 */
	public byte getWorldFiller(int x, int y) {
		Room currentRoom = world.currentRoom;
		int tx = x < 0 ? 0 : Math.min(x, currentRoom.width - 1);
		int ty = y < 0 ? 0 : Math.min(y, currentRoom.height - 1);
		return currentRoom.fillers[tx][ty];
	}

	/**
	 * <p>计算 Box 的数值. 适用于单一盒子. 不要将不受地形影响的 Foe 的盒子调用该方法.
	 *
	 * <p>以下数值将重新计算:
	 * <li>四面是否碰到边: leftTouched, rightTouched, topTouched, bottomTouched
	 * <li>是否在空中: inAir
	 * </li></p>
	 */
	public Box freshBox(Box box) {
		box.flush();
		Room room = world.currentRoom;

		float xLeft = box.pos.x;
		float xRight = xLeft + box.pos.width;
		float yBottom = box.pos.y;
		float yTop = yBottom + box.pos.height;

		// 盒子左右边所在的格子, 单位: 块
		int ibxLeft = Math.max(pToBlock(xLeft), 0);
		int ibxRight = Math.min(pToBlockLeft(xRight), room.width - 1);
		// 盒子上下边所在的格子, 单位: 块
		int ibyBottom = Math.max(pToBlock(yBottom), 0);
		int ibyTop = Math.min(pToBlockLeft(yTop), room.height - 1);

		// 1. 地形判断
		box.leftTouched = false;
		box.rightTouched = false;
		// 左右 (贴边)
		if (xLeft == ibxLeft) { // 左边刚好是整数
			for (int y = ibyBottom; y <= ibyTop; y++) {
				int terrain = getTerrain(ibxLeft - 1, y);
				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块 (斜边)
					box.leftTouched = true; break;
				}
			}
		}
		if (xRight == ibxRight + 1) { // 右边刚好是整数
			for (int y = ibyBottom; y <= ibyTop; y++) {
				int terrain = getTerrain(ibxRight + 1, y);
				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块 (斜边)
					box.rightTouched = true; break;
				}
			}
		}

		// 下上
		box.bottomTouched = false;
		box.topTouched = false;
		if (yBottom == ibyBottom) { // 下边刚好是整数
			for (int x = ibxLeft; x <= ibxRight; x++) {
				int terrain = getTerrain(x, ibyBottom - 1);
				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块 (斜边)
					box.bottomTouched = true; break;
				} else if (terrain == TERRAIN_LADDER) {
					// 梯子只有最上面那一块能站
					if (getTerrain(x, ibyBottom) != TERRAIN_LADDER
							&& box.gravityDown && climbable) {
						box.bottomTouched = true; break;
					}
				}
			}
		}
		// TODO 先判断 其它实体块  这里不处理将导致洛克跳跃时向上撞到实体块不会磕脑袋
		if (yTop == ibyTop + 1) { // 上边刚好是整数
			for (int x = ibxLeft; x <= ibxRight; x++) {
				int terrain = getTerrain(x, ibyTop + 1);
				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块 (斜边)
					box.topTouched = true; break;
				} else if (terrain == TERRAIN_LADDER) {
					// 梯子只有最上面那一块能站
					if (getTerrain(x, ibyTop) != TERRAIN_LADDER && !box.gravityDown && climbable) {
						box.topTouched = true; break;
					}
				}
			}
		}

		// 2. 地形盒子判断
		tmp.clear();
		world.findOverlapFoeAndPutInList(tmp,
				xLeft - 0.1f,
				yBottom - 0.1f,
				box.pos.width + 0.2f,
				box.pos.height + 0.2f);
		for (MFoe checkBox : tmp) {
			if (checkBox == box) {
				continue;
			}

			int t = checkBox.terrainBySide(box.side, 0);
			if (t == 0) {
				continue;
			}

			float cxLeft = checkBox.pos.x;
			float cxRight = cxLeft + checkBox.pos.width;
			float cyBottom = checkBox.pos.y;
			float cyTop = cyBottom + checkBox.pos.height;

			if ((t & BOX_TERRAIN_SOFT_SOLID) != 0) {
				if (yBottom < cyTop && yTop > cyBottom) {
					// 左右碰撞
					if (xLeft == cxRight) {
						box.leftTouched = true;
					} else if (xRight == cxLeft) {
						box.rightTouched = true;
					}
				} else if (xLeft < cxRight && xRight > cxLeft) {
					// 上下碰撞
					if (yBottom == cyTop) {
						box.bottomTouched = true;
					} else if (yTop == cyBottom) {
						box.topTouched = true;
					}
				}
			} else if ((t & BOX_TERRAIN_STRONG_SOLID) != 0) {
				if (xLeft > cxRight || xRight < cxLeft || yBottom > cyTop || yTop < cyBottom) {
					// 有重叠部分了, 说明 glitch 了
					System.err.println("glitch");
					box.leftTouched = box.rightTouched = box.bottomTouched = box.topTouched = true;
				} else {
					if (yBottom < cyTop && yTop > cyBottom) {
						// 左右碰撞
						if (xLeft == cxRight) {
							box.leftTouched = true;
						} else if (xRight == cxLeft) {
							box.rightTouched = true;
						}
					} else if (xLeft < cxRight && xRight > cxLeft) {
						// 上下碰撞
						if (yBottom == cyTop) {
							box.bottomTouched = true;
						} else if (yTop == cyBottom) {
							box.topTouched = true;
						}
					}
				}
			} else if ((t & BOX_TERRAIN_PLATFORM_UP) != 0) {
				// 可以判断 bottomTouched
				if (xLeft < cxRight && xRight > cxLeft) {
					if (yBottom == cyTop && box.gravityDown) {
						box.bottomTouched = true;
					}
				}
			} else if ((t & BOX_TERRAIN_PLATFORM_DOWN) != 0) {
				// 可以判断 topTouched
				if (xLeft < cxRight && xRight > cxLeft) {
					if (yTop == cyBottom && !box.gravityDown) {
						box.topTouched = true;
					}
				}
			}

			boolean leftWall = leftWall(t, checkBox.orientation);
			boolean rightWall = rightWall(t, checkBox.orientation);

			if (leftWall && rightWall) {
				if (xRight == cxLeft) {
					box.rightTouched = true;
				} else if (xLeft == cxRight) {
					box.leftTouched = true;
				}
			} else if (leftWall) {
				if (xRight == cxLeft) {
					box.rightTouched = true;
				}
			} else if (rightWall) {
				if (xLeft == cxRight) {
					box.leftTouched = true;
				}
			}
		}

		// 是否在空中
		box.inAir = !(box.bottomTouched && box.gravityDown || box.topTouched && !box.gravityDown);
		return box;
	}

	private boolean leftWall(int boxTerrain, boolean orientation) {
		return ((boxTerrain & BOX_TERRAIN_WALL_LEFT) != 0) && orientation ||
				((boxTerrain & BOX_TERRAIN_WALL_RIGHT) != 0) && !orientation;
	}

	private boolean rightWall(int boxTerrain, boolean orientation) {
		return ((boxTerrain & BOX_TERRAIN_WALL_RIGHT) != 0) && orientation ||
				((boxTerrain & BOX_TERRAIN_WALL_LEFT) != 0) && !orientation;
	}

	public void submitFloatBoxMotion(Box box) {
		submitFloatBoxMotion(box, box.velocityX, box.velocityY);
	}

	public void submitFloatBoxMotion(Box box, float deltaX, float deltaY) {
		box.flush();
		box.addAnchorX(deltaX);
		box.addAnchorY(deltaY);
		box.flush();
	}

	/**
	 * 处理 Foe 的移动, 按照盒子中的速度来更新盒子中的 anchor 位置.
	 * 会连带处理盒子推动其它盒子、盒子上方踩着的盒子的相互作用运动
	 */
	public void submitMotion(Box box) {
		if (this.floating) {
			submitFloatBoxMotion(box);
			return;
		}
		submitMotion(box, box.velocityX, box.velocityY);
	}

	/**
	 * <p>处理步骤:
	 * <li>1. 处理接壤的有哪些盒子:
	 * 有谁站在你上面会被算作接壤 (跟随着动);
	 * 其它, 如果盒子将向哪个方向移动, 那个方向接壤的盒子会被挤过去 (推动)
	 * <li>2. 处理左右移动, 站在上面的和左右推动的盒子将一起运动.
	 * 处理移动时, 先计算被推动的盒子, 所有被推动的盒子能移动多少被视为当前平台能移动的最大量;
	 * 另外, 站在上面的盒子将按照算出的最大量施加左右移动效果 (最终可能小于这个最大量)
	 * <li>3. 处理上下移动, 站在上面的和上下推动的盒子将一起运动. 需要重新区分是推动还是跟随着动.
	 * 处理移动时, 先计算被推动的盒子, 所有被推动的盒子能移动多少被视为当前平台能移动的最大量;
	 * 另外, 跟随着动的盒子将按照算出的最大量施加左右移动效果 (最终可能小于这个最大量)
	 * </p>
	 *
	 * TODO: 当前不考虑推动, 只考虑跟随着动
	 * TODO: 当前不考虑递归情况, 只考虑平台和平台上的 Foe (这个 Foe 上面再有角色就不管了)
	 */
	public void submitMotion(Box box, float deltaX, float deltaY) {
		if (this.floating) {
			submitFloatBoxMotion(box, deltaX, deltaY);
			return;
		}

		// 1. 计算哪些是要被跟随着动的、哪些是推动的
		SubmitMotionBundle bundle = new SubmitMotionBundle();
		List<MFoe> foes = new ArrayList<>();
		bundle.tops.put(box, foes);
		neighborTopDragged(box, foes);

		// 2. 处理上下移动  向上的含推动
		checkAndApplyMotionY(box, normal(deltaY), bundle);

		// 3. 处理左右移动
		checkAndApplyMotionX(box, normal(deltaX), bundle);
	}

	private static class SubmitMotionBundle {
		/**
		 * 上方被拖拽的 foe
		 */
		Map<Box, List<MFoe>> tops = new HashMap<>();
	}

	private void checkAndApplyMotionX(Box box, final float deltaX, SubmitMotionBundle bundle) {
		float pxsLeft = box.pos.x; // src
		float pxsRight = pxsLeft + box.pos.width; // src
		float pyBottom = box.pos.y;
		float pyTop = pyBottom + box.pos.height;
		float pToX;

		int bxsLeft = pToBlock(pxsLeft); // 单位：b
		int bxsRight = pToBlockLeft(pxsRight);
		int byBottom = pToBlock(pyBottom);
		int byTop = pToBlockLeft(pyTop);

		if (deltaX < 0) { // 向左
			float pxdLeft = pxsLeft + deltaX; // dest

			// 地形判断 (如果有斜面, 判断会更加复杂)

			int bxdLeft = pToBlockLeft(pxdLeft); // 单位：b
			if (bxsLeft != bxdLeft) { // 出现了跨格子
				LOOP:
				for (int x = bxsLeft - 1; x >= bxdLeft; x--) {
					for (int y = byBottom; y <= byTop; y++) {
						switch (getTerrain(x, y)) { // TODO 其它实体块
						case TERRAIN_SOLID:
							// 最后向左移动的结果就是撞到该格子
							pxdLeft = x + 1;
							break LOOP;
						}
					}
				}
			}

			// 2. 地形盒子判断
			tmp3.clear();
			world.findOverlapFoeAndPutInList(tmp3, pxdLeft, pyBottom, pxsLeft - pxdLeft, pyTop - pyBottom);
			for (MFoe others: tmp3) {
				if (others == box) {
					continue;
				}

				int t = others.terrainBySide(box.side, 0);
				boolean solid = (t & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID)) != 0;
				boolean wall = rightWall(t, others.orientation);
				if (!solid && !wall) {
					continue;
				}

				// TODO others 需要判断, box 扫过的部分和 others 盒子是否有交集
				// TODO 未区分 BOX_TERRAIN_STRONG_SOLID 和 BOX_TERRAIN_SOFT_SOLID
				// 到这里就说明重合了
				float toX = others.pos.x + others.pos.width;
				if (toX > pxsLeft) {
					System.err.println("Glitch 错误");
				} else {
					pxdLeft = toX;
				}
			}

			pToX = pxdLeft;
		} else if (deltaX > 0) { // 向右
			float pxdRight = pxsRight + deltaX; // dest

			// 1. 地形判断

			int bxdRight = pToBlock(pxdRight);
			if (bxsRight != bxdRight) { // 出现了跨格子
				LOOP:
				for (int x = bxsRight + 1; x <= bxdRight; x++) {
					for (int y = byBottom; y <= byTop; y++) {
						switch (getTerrain(x, y)) { // TODO 其它实体块
						case TERRAIN_SOLID:
							// 最后向右移动的结果就是撞到该格子
							pxdRight = x;
							break LOOP;
						}
					}
				}
			}

			// 2. 地形盒子判断
			tmp.clear();
			world.findOverlapFoeAndPutInList(tmp, pxsRight, pyBottom, pxdRight - pxsRight, pyTop - pyBottom);
			for (MFoe others: tmp) {
				if (others == box) {
					continue;
				}

				int t = others.terrainBySide(box.side, 0);
				boolean solid = (t & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID)) != 0;
				boolean wall = leftWall(t, others.orientation);
				if (!solid && !wall) {
					continue;
				}

				// TODO others 需要判断, box 扫过的部分和 others 盒子是否有交集
				// TODO 未区分 BOX_TERRAIN_STRONG_SOLID 和 BOX_TERRAIN_SOFT_SOLID
				// 到这里就说明重合了
				float toRightX = others.pos.x;
				if (toRightX < pxsRight) {
					System.err.println("Glitch 错误");
				} else {
					pxdRight = toRightX;
				}
			}

			pToX = pxdRight - box.pos.width;
		} else {
			// 没有移动
			return;
		}

		// apply
		submitMotionX(box, pToX - box.boxX, bundle);
	}

	private void submitMotionX(Box box, final float pToX, SubmitMotionBundle bundle) {
		float oriX = box.anchorX;
		box.setAnchorX(pToX);
		box.flush();

		// 被拖拽的 box
		List<MFoe> dragged = bundle.tops.get(box);
		if (dragged != null) {
			for (MFoe subFoe : dragged) {
				subFoe.submitMotion(pToX - oriX, 0);
			}
		}
	}

	private void checkAndApplyMotionY(Box box, final float deltaY, SubmitMotionBundle bundle) {
		float pysBottom = box.pos.y; // src
		float pysTop = pysBottom + box.pos.height; // src
		float pxLeft = box.pos.x;
		float pxRight = pxLeft + box.pos.width;
		float pToY;

		int bxLeft = pToBlock(pxLeft);
		int bxRight = pToBlockLeft(pxRight);

		if (deltaY < 0) {
			// 1. 地形判断
			int bysBottom = pToBlock(pysBottom); // 单位：b
			float pydBottom = pysBottom + deltaY; // dest
			int bydBottom = pToBlockLeft(pydBottom);

			if (bysBottom != bydBottom) { // 跨格子
				LOOP:
				for (int y = bysBottom - 1; y >= bydBottom; y--) {
					for (int x = bxLeft; x <= bxRight; x++) {
						switch (getTerrain(x, y)) { // TODO 其它实体块
						case TERRAIN_SOLID:
							// 最后向下移动的结果就是撞到该格子
							pydBottom = y + 1;
							break LOOP;
						case TERRAIN_LADDER:
							// 只有顶端的梯子有效
							if (!box.gravityDown || getTerrain(x, y + 1) == TERRAIN_LADDER) {
								break;
							}
							pydBottom = y + 1;
							break LOOP;
						}
					}
				}
			}

			// 2. 地形盒子判断
			tmp2.clear();
			world.findOverlapFoeAndPutInList(tmp2, pxLeft, pydBottom, box.pos.width, pysBottom - pydBottom);
			int mask = BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID;
			if (box.gravityDown) {
				mask |= BOX_TERRAIN_PLATFORM_UP;
			}

			for (MFoe others: tmp2) {
				if (others == box) {
					continue;
				}

				int t = others.terrainBySide(box.side, 0) & mask;
				if (t == 0) {
					continue;
				}

				// TODO others 需要判断, box 扫过的部分和 others 盒子是否有交集
				// TODO 未区分 BOX_TERRAIN_STRONG_SOLID 和 BOX_TERRAIN_SOFT_SOLID
				// 到这里就说明重合了
				float toY = others.pos.y + others.pos.height;
				if (toY <= pysBottom) {
					pydBottom = toY;
				}
			}

			pToY = pydBottom;
		} else if (deltaY > 0) {
			// 1. 地形判断
			int bysTop = pToBlockLeft(pysTop);
			float pydTop = pysTop + deltaY; // dest
			int bydTop = pToBlockLeft(pydTop);

			if (bysTop != bydTop) { // 跨格子
				LOOP:
				for (int y = bysTop + 1; y <= bydTop; y++) {
					for (int x = bxLeft; x <= bxRight; x++) {
						switch (getTerrain(x, y)) { // TODO 其它实体块
						case TERRAIN_SOLID:
							// 最后向上移动的结果就是撞到该格子
							pydTop = y;
							break LOOP;
						case TERRAIN_LADDER:
							// 只有顶端的梯子有效
							if (box.gravityDown || getTerrain(x, y + 1) == TERRAIN_LADDER) {
								break;
							}
							pydTop = y;
							break LOOP;
						}
					}
				}
			}

			// 2. 地形盒子判断
			tmp3.clear();
			world.findOverlapFoeAndPutInList(tmp3, pxLeft, pysTop, box.pos.width, pydTop - pysTop);
			int mask = BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID;
			if (!box.gravityDown) {
				mask |= BOX_TERRAIN_PLATFORM_DOWN;
			}
			for (MFoe others: tmp3) {
				if (others == box) {
					continue;
				}

				int t = others.terrainBySide(box.side, 0) & mask;
				if (t == 0) {
					continue;
				}

				// TODO others 需要判断, box 扫过的部分和 others 盒子是否有交集
				// TODO 未区分 BOX_TERRAIN_STRONG_SOLID 和 BOX_TERRAIN_SOFT_SOLID
				// 到这里就说明重合了
				float toY = others.pos.y;
				if (toY >= pysTop) {
					pydTop = toY;
				}
			}

			pToY = pydTop - box.pos.height;
		} else {
			// 没有移动
			return;
		}

		// apply
		submitMotionY(box, pToY - box.boxY, bundle);
	}

	private void submitMotionY(Box box, final float pToY, SubmitMotionBundle bundle) {
		float oriY = box.anchorY;
		box.setAnchorY(pToY);
		box.flush();

		// 被拖拽的 box
		List<MFoe> dragged = bundle.tops.get(box);
		if (dragged != null) {
			for (MFoe subFoe : dragged) {
				subFoe.submitMotion(0, pToY - oriY);
			}
		}
	}

	/**
	 * <p>查询和 box 上方接壤的所有能被传动 (拖动) 的 box.
	 * <p>判断包括地形, 也就是说, 你必须推得动它; 如果双方视对方的地形为 empty, 那它将不会加入到查询结果中.
	 * 你指定的 box 作为平台可以计入查询结果, 但别的 box 是否是平台将被忽略.
	 * </p>
	 */
	public void neighborTopDragged(Box box, List<MFoe> top) {
		box.flush();

		float pxLeft = box.pos.x;
		float pyTop = box.pos.y + box.pos.height;
		tmp2.clear();
		world.findOverlapFoeAndPutInList(TerrainHandler.tmp2, pxLeft, pyTop - 0.1f, box.pos.width, pyTop + 0.1f);
		for (MFoe others: tmp2) {
			if (others == box) {
				continue;
			}
			if (others.pos.y != pyTop) { // 这里之后能确定 checkBox 踩着 box
				continue;
			}

			int boxTerrain = box.terrainBySide(others.side, 0);

			boolean b = (boxTerrain & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID | BOX_TERRAIN_PLATFORM_UP)) != 0;
			if (b) {
				top.add(others);
			}
		}
	}

	public boolean isBoxOverlap(Box box1, Box box2) {
		return box1.flush().pos.overlaps(box2.flush().pos);
	}

	/**
	 * 判断是否和地形或地形盒子 Foe 重合
	 * @return
	 *   重合了返回 true
	 */
	public boolean isBoxOverlap(Box box) {
		box.flush();

		// 地形
		float pxLeft = box.pos.x;
		float pxRight = pxLeft + box.pos.width;
		float pyBottom = box.pos.y;
		float pyTop = pyBottom + box.pos.height;

		int bxLeft = pToBlock(pxLeft);
		int bxRight = pToBlockLeft(pxRight);
		int byBottom = pToBlock(pyBottom);
		int byTop = pToBlockLeft(pyTop);

		for (int x = bxLeft; x <= bxRight; x++) {
			for (int y = byBottom; y <= byTop; y++) {
				byte terrain = getTerrain(x, y);

				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块
					return true;
				}
			}
		}

		// 地形盒子
		tmp.clear();
		world.findOverlapFoeAndPutInList(tmp, pxLeft, pyBottom, box.pos.width, box.pos.height);
		for (MFoe others: tmp) {
			if (others == box) {
				continue;
			}

			int t = others.terrainBySide(box.side, 0) & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID);
			if (t == 0) {
				continue;
			}

			// 到这里就说明重合了
			return true;
		}
		return false;
	}

	/**
	 * 本方法忽略地形盒子
	 */
	public Iterator<TerrainParam> checkBoxTerrain(Box box) {
		box.flush();

		// 地形
		float pxLeft = box.pos.x;
		float pxRight = pxLeft + box.pos.width;
		float pyBottom = box.pos.y;
		float pyTop = pyBottom + box.pos.height;

		final int bxLeft = pToBlock(pxLeft);
		final int bxRight = pToBlockLeft(pxRight);
		final int byBottom = pToBlock(pyBottom);
		final int byTop = pToBlockLeft(pyTop);

		return new Iterator<TerrainParam>() {
			final TerrainParam def = new TerrainParam();
			int x = bxLeft, y = byBottom;

			@Override
			public boolean hasNext() {
				return y <= byTop;
			}

			@Override
			public TerrainParam next() {
				def.bx = x;
				def.by = y;
				def.terrain = getTerrain(x, y);

				x++;
				if (x > bxRight) {
					x = bxLeft;
					y++;
				}
				return def;
			}
		};
	}

	public boolean glitchFix(Box box) {
		return glitchFix(box, true, true, true, true);
	}

	/**
	 * <p>盒子的位置重合修正.
	 * <p>由于一系列原因导致盒子与不能重合的物体重合了 (比如地形、不穿透刚体等).
	 * 造成该类原因有些是陷阱、不穿透怪物等自身运动导致和其它物体重合,
	 * 有些是浮点数的计算精度缺陷 (比如 16 计算成 15.999999)
	 * <p>修正的方式是将该盒子移出重合区. 本函数将尝试向上下左右四个方向将物体移出重合区,
	 * 但最大移动距离设置为 1 格 (以下). 如果盒子无法移出重合区, 将会返回 false,
	 * 让盒子的持有者自行决定如何处理.
	 * </p>
	 * @return
	 *   当出现无法修复的情况, 返回 false
	 */
	public boolean glitchFix(Box box,
							 boolean leftAllowed, boolean rightAllowed, boolean upAllowed, boolean downAllowed) {
		box.flush();
		if (floating) {
			return true;
		}

		// 判断重合
		float pxLeft = box.pos.x;
		float pxRight = pxLeft + box.pos.width;
		float pyBottom = box.pos.y;
		float pyTop = pyBottom + box.pos.height;

		int bxLeft = pToBlock(pxLeft);
		int bxRight = pToBlockLeft(pxRight);
		int byBottom = pToBlock(pyBottom);
		int byTop = pToBlockLeft(pyTop);

		List<TerrainParam> overlaps = new ArrayList<>();

		for (int x = bxLeft; x <= bxRight; x++) {
			for (int y = byBottom; y <= byTop; y++) {
				byte terrain = getTerrain(x, y);

				if (terrain == TERRAIN_SOLID) { // TODO 其它实体块
					overlaps.add(new TerrainParam(x, y, terrain));
				}
			}
		}

		// TODO 除了地形以外的重合判定
		float plDelta = 0, prDelta = 0, puDelta = 0, pdDelta = 0;
		if (overlaps.size() != 0) {
			float rightMax = Integer.MIN_VALUE, leftMin = Integer.MAX_VALUE,
					topMax = Integer.MIN_VALUE, bottomMin = Integer.MAX_VALUE;

			// 修正部分
			// 如果采用左右上下移动, 将最少使用的偏移量. 下面四个参数是记录偏移量的. 单位: p
			// 四个偏移量超过 1 就不允许
			for (TerrainParam t: overlaps) {
				if (t.terrain == TERRAIN_SOLID) { // TODO 其它实体块
					float ptLeft = t.bx;
					float ptRight = t.bx + 1;
					float ptTop = t.by + 1;
					float ptBottom = t.by;

					rightMax = Math.max(ptRight, rightMax);
					leftMin = Math.min(ptLeft, leftMin);
					topMax = Math.max(ptTop, topMax);
					bottomMin = Math.min(ptBottom, bottomMin);
				}
			}

			plDelta = Math.abs(pxRight - leftMin); // 向左推
			prDelta = Math.abs(pxLeft - rightMax); // 向右推
			puDelta = Math.abs(pyBottom - topMax); // 向上推
			pdDelta = Math.abs(pyTop - bottomMin); // 向下推
		}

		// 地形盒子
		tmp3.clear();
		world.findOverlapFoeAndPutInList(tmp3, pxLeft, pyBottom, box.pos.width, box.pos.height);
		for (MFoe others: tmp3) {
			if (others == box) {
				continue;
			}

			int t = others.terrainBySide(box.side, 0) & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID);
			if (t == 0) {
				continue;
			}

			// 到这里就说明重合了
			plDelta = Math.max(pxRight - others.pos.x, plDelta);
			prDelta = Math.max(others.pos.x + others.pos.width - pxLeft, prDelta);
			puDelta = Math.max(others.pos.y + others.pos.height - pyBottom, puDelta);
			pdDelta = Math.max(pyTop - others.pos.y, pdDelta);
		}

		if (plDelta == 0 && prDelta == 0 && puDelta == 0 && pdDelta == 0) {
			return true;
		}
		if (!leftAllowed && !rightAllowed && !upAllowed && !downAllowed) {
			return false;
		}

		// 按左右上下取最小值
		boolean l = leftAllowed && plDelta < 1,
				r = rightAllowed && prDelta < 1,
				u = upAllowed && puDelta < 1,
				d = downAllowed && pdDelta < 1;
		int loopCount = l ? 1 : 0;
				loopCount = r ? loopCount + 1 : loopCount;
				loopCount = u ? loopCount + 1 : loopCount;
				loopCount = d ? loopCount + 1 : loopCount;

		float delta ; // 记录移动的数量
		// 尝试移动之后
		float pxStart, pyStart;

		for (int j = 0; j < loopCount; j++) {
			int choose = 0; // 1:左, 2:右, 3:上, 4:下
			delta = 1;

			if (l && plDelta < delta) {
				choose = 1;
				delta = plDelta;
			}
			if (r && prDelta < delta) {
				choose = 2;
				delta = prDelta;
			}
			if (u && puDelta < delta) {
				choose = 3;
				delta = puDelta;
			}
			if (d && pdDelta < delta) {
				choose = 4;
				delta = pdDelta;
			}

			// 执行
			pxStart = pxLeft;
			pyStart = pyBottom;
			switch (choose) {
			case 1:
				pxStart = pxLeft - delta;
				l = false;
				break;
			case 2:
				pxStart = pxLeft + delta;
				r = false;
				break;
			case 3:
				pyStart = pyBottom + delta;
				u = false;
				break;
			case 4:
				pyStart = pyBottom - delta;
				d = false;
				break;
			default:
				return false;
			}

			boolean overlapped = false;

			// 如果移动之后, 是否还和地形有重叠
			int pFixLeft = pToBlock(pxStart);
			int pFixRight = pToBlockLeft(pxStart + box.pos.width);
			int pFixBottom = pToBlock(pyStart);
			int pFixTop = pToBlockLeft(pyStart + box.pos.height);
			CHECK_LOOP: for (int x = pFixLeft; x <= pFixRight; x++) {
				for (int y = pFixBottom; y <= pFixTop; y++) {
					byte terrain = getTerrain(x, y);
					if (terrain == TERRAIN_SOLID) { // TODO 其它实体块
						overlapped = true;
						break CHECK_LOOP;
					}
				}
			}
			if (overlapped) {
				continue;
			}

			// 如果移动之后, 是否还和地形盒子有重叠
			tmp2.clear();
			world.findOverlapFoeAndPutInList(tmp2, pxStart, pyStart, box.pos.width, box.pos.height);

			for (MFoe others: tmp2) {
				if (others == box) {
					continue;
				}

				int t = others.terrainBySide(box.side, 0) & (BOX_TERRAIN_STRONG_SOLID | BOX_TERRAIN_SOFT_SOLID);
				if (t == 0) {
					continue;
				}

				// 到这里就说明重合了
				overlapped = true;
				break;
			}
			if (overlapped) {
				continue;
			}

			// 修正成功
			switch (choose) {
			case 1: case 2:
				box.setAnchorX(pxStart - box.boxX);
				return true;
			case 3: case 4:
				box.setAnchorY(pyStart - box.boxY);
				return true;
			}
		}

		return false;
	}

	public static class TerrainParam {
		public int bx, by;
		public byte terrain;
		public TerrainParam() {
		}
		public TerrainParam(int bx, int by, byte terrain) {
			this.bx = bx;
			this.by = by;
			this.terrain = terrain;
		}
	}
}
